package de.uni_mannheim.informatik.ai.dame_ai.dame.ui;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

import de.uni_mannheim.informatik.ai.dame_ai.dame.application.DameFacade;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.Board;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.GameStatus;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.Piece;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.Player;
import de.uni_mannheim.informatik.ai.dame_ai.dame.domain.Square;
import de.uni_mannheim.informatik.ai.dame_ai.main.application.GameSettings;
import de.uni_mannheim.informatik.ai.dame_ai.main.application.MainPlayer;




public class DameGui extends JPanel implements Observer{
	private JLayeredPane layeredPane;
	private boolean debug = false;
	public static final int BOARD = 0;
	public static final int LEFT = 1;
	public static final int RIGHT = 2;
	public static final int TOP = 3;
	public static final int BOTTOM = 4;
	private static final int EMPTY = Piece.EMPTY;
	private static final int WHITE = Piece.WHITE;
	private static final int WHITEDAME = Piece.WHITEDAME;
	private static final int BLACK = Piece.BLACK;
	private static final int BLACKDAME = Piece.BLACKDAME;
	int width;
	int height;
	Board currentBoard;
	private boolean start = true;
	boolean selectionPossible;
	BoardComponent boardCom;
	private Player currentPlayer;
	Player[] humanPlayers;
	boolean isTurn;
	boolean gameOver;
	boolean isReplay;
	ImageWrapper[] images = new ImageWrapper[5];
	BufferedImage[] pieces = new BufferedImage[4];
	Rectangle sidePanelBounds;
	SidePanel sidePanel;
	JPanel mainPanel;
	Rectangle mainPanelBounds;
	DameFacade application;
	Player[] players;
	GameSettings data;
	/**
	 * Creates new Dame Gui and initializes Facade for the ui
	 * @param height
	 * @param width
	 */
	public DameGui(int height, int width, Player[] players, DameFacade application, GameSettings data) {
		this.players = players;
		this.application = application;
		setGameSettings(data);
		setLayout(null);
		setBackground(Color.yellow);
		String designPath = "media" + File.separator + "dame" + File.separator
				+ "design" + File.separator;
		setSize(width, height);
		try {
			for (int i = 0; i < 5; i++) {
				images[i] = new ImageWrapper(i);
			}
			images[0].img = ImageIO.read(new File(designPath + "board.png"));
			images[1].img = ImageIO.read(new File(designPath + "left.png"));
			images[2].img = ImageIO.read(new File(designPath + "right.png"));
			images[3].img = ImageIO.read(new File(designPath + "top.png"));
			images[4].img = ImageIO.read(new File(designPath + "bottom.png"));
			pieces[WHITE] = ImageIO.read(new File(designPath + "white.png"));
			pieces[WHITEDAME] = ImageIO.read(new File(designPath
					+ "whiteDame.png"));
			pieces[BLACK] = ImageIO.read(new File(designPath + "black.png"));
			pieces[BLACKDAME] = ImageIO.read(new File(designPath
					+ "blackDame.png"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		initBoard();
		
	}
	private void setGameSettings(GameSettings newData){
		data = newData;
		MainPlayer[] mainPlayers = data.getPlayers();
		Player[] players = new Player[2];
		players[0]= new Player(mainPlayers[0].getNumber(),mainPlayers[0].getNumber(),mainPlayers[0].getName(),mainPlayers[0].isHuman());
		players[1]= new Player(mainPlayers[1].getNumber(),mainPlayers[1].getNumber(),mainPlayers[1].getName(),mainPlayers[1].isHuman());
		isReplay = data.isReplay();
		this.players= players;
		ArrayList<Player> humanTempPlayers = new ArrayList<Player>();
		for(Player player : players){
			if(player.isHuman()){
				humanTempPlayers.add(player);
			}
		}
		humanPlayers = humanTempPlayers.toArray(new Player[humanTempPlayers.size()]);
	}
	private void resetSidepanel(){
			if(sidePanel != null){
				remove(sidePanel);
			}
			sidePanel = new SidePanel(sidePanelBounds, isReplay,players,pieces,this);
			add(sidePanel);
			javax.swing.SwingUtilities.invokeLater(new Runnable() {
		           public void run() {
		        	recalulateBounds();
		        	repaint();

		           }
				}
			);
	}
	synchronized public void nextMove(){
		if(isReplay){
			application.nextMove();
		}
	}
	synchronized public void lastMove(){
		if(isReplay){
			if(debug)System.out.println("LastMove: "+application.lastMove());
		}
	}
	@Override
	public void paint(Graphics g) {
		super.paint(g);
		repaintBitmapComponents();
		if(debug)System.out.println("validated");
	}

	private void initBoard() {
		
		calculateBounds(getWidth(), getHeight());
		mainPanel = new JPanel();
		layeredPane = new JLayeredPane();
		layeredPane.setBounds(mainPanelBounds);
		mainPanel.setBounds(mainPanelBounds);
		mainPanel.setBackground(Color.DARK_GRAY);
		sidePanel = new SidePanel(sidePanelBounds, isReplay,players,pieces,this);
		sidePanel.setBounds(sidePanelBounds);
		createBitmapComponents();
		start = false;
		if(debug)System.out.println("create Bitmaps");
		paintImages(layeredPane);
		mainPanel.add(layeredPane);
		add(sidePanel);
		add(mainPanel);
		
		sidePanel.setVisible(true);
		mainPanel.setVisible(true);
		
		if(debug)System.out.println("paint Images");
	}

	private void paintImages(JLayeredPane pane) {
		for (int i = 0; i < 5; i++) {
			pane.add(images[i].bitCom, new Integer(0));
		}
	}
	private int horizontalOffset = 0;
	private int verticalOffset = 0;
	/**
	 * Create the Background images and the Board component
	 */
	private void createBitmapComponents() {
		for (int i = 0; i < 5; i++) {
			if (i == BOARD) {
				boardCom = new BoardComponent(images[i].img, images[i].bounds,
						this);
				images[i].bitCom = boardCom;
				boardCom.insertPieceImages(pieces);

			} else {
				images[i].bitCom = new BitmapComponent(images[i].img,
						images[i].bounds);
			}

		}
	}

	protected boolean madeSelection = false;
	protected int[] possiblePositions;
	protected int selectedPosition;
	/**
	 * Method checks if the selected position belongs to player
	 * @param type selected piece
	 * @return Returns true when selected piece belongs to current player
	 */
	private boolean playersTurn(short type){
		if(debug)System.out.println("currentPlayer: "+currentPlayer.getNumber()+" Player color: "+currentPlayer.getColor()+" Type: "+type);
		if(currentPlayer.getColor()==Player.BLACK && (type == BLACK || type == BLACKDAME))return true;
		else if (currentPlayer.getColor() == Player.WHITE && (type == WHITE || type == WHITEDAME))return true;
		else return false;
	}
	/**
	 * Handles logic about what happens wenn a piece is clicked
	 * @param position The selected position
	 */
	public void handleBoardEvent(int position) {
		if(debug)System.out.println("isTurn: "+isTurn);
		if (position > 0 && position < 64 && isTurn) {
			short type = currentBoard.getPiecesStatus()[position].getType();
			if(debug)System.out.println("Selection possible: "+selectionPossible+" selectedposition: "+selectedPosition+" playerTurn: "+playersTurn(type));
			if (selectionPossible && position == selectedPosition) {
				application.deselect();
			}else if (selectionPossible && selectedPosition==-1) {
				if (playersTurn(type) && !data.isReplay()) {
					application.selectPiece(position);
				} else {
					if(debug)System.out.println("Selection not Possible");
				}
			}else if (application.isPossibleMove(selectedPosition, position)&&!data.isReplay()) {
				application.makeMove(selectedPosition, position);
				
				/*boardCom.setPieceStatus(application.getPieceStatus());
				boardCom.setHighlightedFields(application
						.getHighlightedSquares());*/
			}
		}
	}

	private void repaintBitmapComponents() {
		for (int i = 0; i < 5; i++) {
			images[i].bitCom.setNewBounds(images[i].bounds);
		}
	}
	public void recalulateBounds(){
		sidePanel.recreate();
		calculateBounds(getWidth(),getHeight());
		sidePanel.setBounds(sidePanelBounds);
		layeredPane.setBounds(mainPanelBounds);
		mainPanel.setBounds(mainPanelBounds);
	}
	/**
	 * Calculate the bounds of the single background components
	 * @param width
	 * @param height
	 */
	private void calculateBounds(int width, int height) {
		sidePanelBounds = new Rectangle(width*3/4,0,width/4,height);
		mainPanelBounds = new Rectangle(0,0,width-sidePanelBounds.width,height);
		width -= sidePanelBounds.width;
		int startPosition = 0;
		int startYPosition = 0;
		int imagesWidth = images[TOP].img.getWidth();
		int imagesHeight = images[TOP].img.getHeight()
				+ images[LEFT].img.getHeight() + images[BOTTOM].img.getHeight();
		double ratio = Math.min((double) (width) / (double) imagesWidth,
				(double) (height) / (double) imagesHeight) - 0.05;
		int verticalBorder = (int) Math.floor(Math.min(images[TOP].img
				.getHeight(), images[BOTTOM].img.getHeight())
				* ratio);
		int horizontalBorder = (int) Math.floor(Math.min(images[LEFT].img
				.getWidth(), images[RIGHT].img.getWidth())
				* ratio);
		int boardSize = (int) Math.floor(Math.max(
				images[BOARD].img.getHeight(), images[BOARD].img.getWidth())
				* ratio);
		imagesWidth = 2 * horizontalBorder + boardSize;
		imagesHeight = 2 * verticalBorder + boardSize;
		horizontalOffset = width - imagesWidth;
		verticalOffset = height - imagesHeight;
		startPosition = horizontalOffset / 2;
		startYPosition = verticalOffset / 2;
		for (int i = 0; i < 5; i++) {
			if (i == TOP) {
				images[i].bounds = new Rectangle(startPosition, startYPosition, imagesWidth,
						verticalBorder);
			} else if (i == LEFT) {
				images[i].bounds = new Rectangle(startPosition, verticalBorder+startYPosition,
						horizontalBorder, boardSize);
			} else if (i == BOARD) {
				images[i].bounds = new Rectangle(horizontalBorder
						+ startPosition, verticalBorder+startYPosition, boardSize, boardSize);
			} else if (i == RIGHT) {
				images[i].bounds = new Rectangle(horizontalBorder + boardSize
						+ startPosition, verticalBorder+startYPosition, horizontalBorder,
						boardSize);
			} else if (i == BOTTOM) {
				images[i].bounds = new Rectangle(startPosition, verticalBorder+startYPosition
						+ boardSize, imagesWidth, verticalBorder);
			}
		}
	}
	/**
	 * ImageWrapper to easier handle data
	 * @author Mataa
	 *
	 */
	class ImageWrapper {
		public BufferedImage img;
		public BitmapComponent bitCom;
		public Rectangle bounds;
		public int name;

		public ImageWrapper() {

		}

		public ImageWrapper(int name) {
			this.name = name;
		}
	}
	/**
	 * Get raw data for representation
	 * @param squares Current Board
	 * @return Array of booleans, true meaning it is highlighted
	 */
	public boolean[] getHighlightedSquares(Square[] squares) {
		boolean[] highlightedSquares = new boolean[Board.BOARDSQUARES];
		for (int i = 0; i < highlightedSquares.length; i++) {
			highlightedSquares[i] = squares[i].isHighlighted();
		}
		return highlightedSquares;
	}
	@Override
	public void update(Observable arg0, Object arg1) {
		if(debug)System.out.println("new Status");
		if(arg1 instanceof GameStatus && arg1 != null) {
			GameStatus temp = (GameStatus)arg1;
			currentPlayer = temp.getCurrentPlayer();
			if(debug)if(debug)System.out.println("New Current Player: "+currentPlayer);
			currentBoard = temp.getBoard();
			short[] pieces = getPieceStatus(currentBoard.getPiecesStatus());
			boardCom.setPieceStatus(pieces);
			boardCom.setHighlightedFields(getHighlightedSquares(temp.getBoard().getBoard()));
			selectionPossible= temp.isSelectionPossible();
			selectedPosition = temp.getSelectedPiece();
			isTurn = false;
			GameSettings newData = temp.getData();
			if(!data.equals(newData)){
				System.err.print("resetting Side Panel-start");
				boolean resetSidePanel = false;
				if(isReplay != newData.isReplay()){
					resetSidePanel = true;
				}
				setGameSettings(newData);
				if(resetSidePanel){
					this.resetSidepanel();
					System.err.print("resetting Side Panel");
				}
			}
			for(Player player : humanPlayers){
				if(player.equals(currentPlayer))isTurn = true;
			}
			repaint();
		}
	}
	/**
	 * Convert Piece array to short array
	 * @param game
	 * @return
	 */
	public short[] getPieceStatus(Piece[] game) {
		short[] pieceStatus = new short[Board.BOARDSQUARES];
		if(debug)System.out.println("PieceStatusLength: "+pieceStatus.length+" gameLength: "+game.length);
		for (int i = 0; i < pieceStatus.length; i++) {
			pieceStatus[i] = game[i].getType();
		}
		return pieceStatus;
	}
	public boolean toggleAutomaticReplay() {
	
		return application.toggleAutomaticReplay();
	}

}
